# SPDX-License-Identifier: Apache-2.0

# CMake version 3.20 is the real minimum supported version.
#
# Under these restraints we use a second 'cmake_minimum_required'
# invocation in every toplevel CMakeLists.txt.
cmake_minimum_required(VERSION 3.20.0)

include(${CMAKE_CURRENT_LIST_DIR}/scripts/boilerplate.cmake)

project(seminix-runtime VERSION ${SEMINIX_VERSION})

add_library(kernel_interface INTERFACE)

target_compile_options(kernel_interface INTERFACE $<$<COMPILE_LANGUAGE:ASM>:-D__ASSEMBLY__>)
target_compile_options(kernel_interface INTERFACE "SHELL: -imacros ${KCONFIG_H}")

target_include_directories(kernel_interface SYSTEM INTERFACE ${NOSTDINC})
target_include_directories(kernel_interface INTERFACE ${SEMINIX_BINARY_DIR}/include)

kernel_compile_definitions(__KERNEL__)
kernel_cc_options(-fdiagnostics-color=always)
kernel_cc_options(-fmacro-prefix-map=${SEMINIX_SOURCE_DIR}=kernel/=)

target_include_directories(kernel_interface
  INTERFACE
  ${SEMINIX_SOURCE_DIR}/arch/${ARCH}/include/uapi
  ${SEMINIX_SOURCE_DIR}/arch/${ARCH}/include/generated/uapi
  ${SEMINIX_SOURCE_DIR}/include/uapi
  ${SEMINIX_SOURCE_DIR}/include/generated/uapi
  ${SEMINIX_SOURCE_DIR}/arch/${ARCH}/include
  ${SEMINIX_SOURCE_DIR}/arch/${ARCH}/include/generated
  ${SEMINIX_SOURCE_DIR}/include
)

kernel_cc_options(-fno-delete-null-pointer-checks)
kernel_cc_options(
  -Wno-frame-address
  -Wno-format-truncation
  -Wno-format-overflow
  -Wno-address-of-packed-member
)

if(CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE)
  kernel_cc_options(-O2)
elseif(CONFIG_CC_OPTIMIZE_FOR_SIZE)
  kernel_cc_options(-Os)
endif()

if(CONFIG_CC_IS_GCC)
  kernel_cc_options(-param=allow-store-data-races=0 -fno-allow-store-data-races)
endif()

if(CONFIG_READABLE_ASM)
  kernel_cc_options(-fno-reorder-blocks -fno-ipa-cp-clone -fno-partial-inlining)
endif()

if(CONFIG_FRAME_WARN)
  kernel_cc_options(-Wframe-larger-than=${CONFIG_FRAME_WARN})
endif()

if(CONFIG_STACKPROTECTOR)
  kernel_cc_options(-fstack-protector)
elseif(CONFIG_STACKPROTECTOR_STRONG)
  kernel_cc_options(-fstack-protector-strong)
else()
  kernel_cc_options(-fno-stack-protector)
endif()

if(CONFIG_CC_IS_CLANG)
  kernel_cc_options(-Qunused-arguments -Wno-gnu)
else()
  kernel_cc_options(-Wno-main)
endif()

if(CONFIG_FRAME_POINTER)
  kernel_cc_options(-fno-omit-frame-pointer -fno-optimize-sibling-calls)
else()
  kernel_cc_options(-fomit-frame-pointer)
endif()

kernel_cc_options(-fno-stack-clash-protection)

if(CONFIG_DEBUG_SECTION_MISMATCH)
  kernel_cc_options(-fno-inline-functions-called-once)
endif()

if(CONFIG_LD_DEAD_CODE_DATA_ELIMINATION)
  kernel_cc_options(-ffunction-sections -fdata-sections)
endif()

if(CONFIG_LTO_CLANG)
  kernel_cc_options(-fvisibility=hidden)
endif()

if(CONFIG_DEBUG_FORCE_FUNCTION_ALIGN_64B)
  kernel_cc_options(-falign-functions=64)
endif()

kernel_cc_options(-nostdinc)

kernel_cc_options(
  -Wdeclaration-after-statement
  -Wvla -Wno-pointer-sign -Wcast-function-type
  -Wno-stringop-truncation -Wno-stringop-overflow -Wno-restrict
  -fno-strict-overflow -fno-stack-check
  -Werror=date-time -Werror=incompatible-pointer-types -Werror=designated-init
)

set(KERNEL_TEXT_OFFSET  ${CONFIG_TEXT_OFFSET})

add_library(seminix-crt STATIC)
target_link_libraries(seminix-crt PRIVATE kernel_interface)
function(kernel_target_sources sources)
  target_sources(seminix-crt PRIVATE ${sources} ${ARGN})
endfunction()

add_library(seminix-crt-lib STATIC)
target_link_libraries(seminix-crt-lib PRIVATE kernel_interface)
function(kernel_lib_sources sources)
  target_sources(seminix-crt-lib PRIVATE ${sources} ${ARGN})
endfunction()

add_subdirectory(arch/${ARCH})

kernel_target_sources(arch/${ARCH}/kernel/head.S)
set_source_files_properties(
  arch/${ARCH}/kernel/head.S
  PROPERTIES COMPILE_DEFINITIONS "TEXT_OFFSET=${KERNEL_TEXT_OFFSET}"
)

set(ASM_OFFSETS_LIB asm_offsets)
# Set some phony targets to collect dependencies
set(ASM_OFFSETS_H_TARGET     asm_offsets_h)
set(ASM_OFFSETS_C_PATH ${SEMINIX_SOURCE_DIR}/arch/${ARCH}/kernel/asm-offsets.c)
set(ASM_OFFSETS_H_PATH ${SEMINIX_BINARY_DIR}/include/generated/asm-offsets.h)

add_library(${ASM_OFFSETS_LIB} OBJECT ${ASM_OFFSETS_C_PATH})
target_link_libraries(${ASM_OFFSETS_LIB} kernel_interface)
set_target_properties(${ASM_OFFSETS_LIB} PROPERTIES COMPILE_OPTIONS "-S")

add_custom_command(
  OUTPUT ${ASM_OFFSETS_H_PATH}
  COMMAND ${PYTHON_EXECUTABLE} ${SEMINIX_SOURCE_DIR}/scripts/asm_offset_h.py
  -n "__ASM_OFFSETS_H_"
  -i $<TARGET_OBJECTS:${ASM_OFFSETS_LIB}>
  -o ${ASM_OFFSETS_H_PATH}
  DEPENDS
  ${ASM_OFFSETS_LIB}
  $<TARGET_OBJECTS:${ASM_OFFSETS_LIB}>
  WORKING_DIRECTORY
  ${SEMINIX_BINARY_DIR}
)
add_custom_target(${ASM_OFFSETS_H_TARGET} DEPENDS ${ASM_OFFSETS_H_PATH})

set(TIMECONST_BC_PATH ${SEMINIX_SOURCE_DIR}/timeconst.bc)
set(TIMECONST_H_PATH ${SEMINIX_BINARY_DIR}/include/generated/timeconst.h)

add_custom_command(
  OUTPUT ${TIMECONST_H_PATH}
  COMMAND ${SHELL} ${SEMINIX_SOURCE_DIR}/scripts/timeconst.sh
  ${CONFIG_HZ}
  ${BC}
  ${TIMECONST_BC_PATH}
  ${TIMECONST_H_PATH}
  WORKING_DIRECTORY
  ${SEMINIX_BINARY_DIR}
)
add_custom_target(timeconst_h DEPENDS ${TIMECONST_H_PATH})

add_custom_target(kernel_generated_headers)
add_dependencies(kernel_generated_headers
  asm_offsets_h
  timeconst_h
)
add_dependencies(asm_offsets_h timeconst_h)
add_dependencies(seminix-crt kernel_generated_headers)
add_dependencies(seminix-crt asm_offsets_h)
add_dependencies(seminix-crt-lib asm_offsets_h)

configure_kernel_linker_script(
  ${SEMINIX_BINARY_DIR}/linker.lds
  "-D__ASSEMBLY__;-DLINKER_SCRIPT;-U${ARCH};-D__KERNEL__;-DTEXT_OFFSET=${KERNEL_TEXT_OFFSET}"
  kernel_generated_headers
)
add_custom_target(
  linker_kernel_prebuilt_script_target
  DEPENDS
  ${SEMINIX_BINARY_DIR}/linker.lds
)
add_dependencies(seminix-crt linker_kernel_prebuilt_script_target)

add_subdirectory(kernel)
add_subdirectory(lib)
